#include "EPSG/Cube.h"

#include <stdio.h>

/* System Includes */
#ifdef WIN32
#include <Windows.h>
#endif

/* OpenGL Includes */
#include <GL/GL.h>

EPSG::Cube::Cube()
{
	setDimensions( 1, 1, 1 );

	_segX = 5;
	_segY = 5;
	_segZ = 5;

	tesselate();
}

EPSG::Cube::Cube( float dx, float dy, float dz )
{
	setDimensions( dx, dy, dz );

	_segX = 5;
	_segY = 5;
	_segZ = 5;

	tesselate();
}

void EPSG::Cube::draw( bool autoTexCoord /*= true */ )
{
	glPushAttrib( GL_CURRENT_BIT );
	glBegin(GL_QUADS);

	if( autoTexCoord )
		drawWithAutoTexCoord();
	else
		drawWithNoTexCoord();

	glEnd();
	glPopAttrib();
}

void EPSG::Cube::setDimensions( float dx, float dy, float dz )
{
	if( dx < 0 || dy < 0 || dz < 0 )
	{
		printf( "ERROR: Invalid cube size!\n" );
		return;
	}

	_dx = dx;
	_dy = dy;
	_dz = dz;

	tesselate();
}

void EPSG::Cube::setSegments( int sx, int sy, int sz )
{
	if( sx < 1 || sy < 1 || sz < 1 )
	{
		printf( "ERROR: Invalid cube segments!\n" );
		return;
	}

	_segX = sx + 1;
	_segY = sy + 1;
	_segZ = sz + 1;

	tesselate();
}

void EPSG::Cube::drawWithAutoTexCoord()
{
	int inx;
	Vec3<float> v0;
	Vec3<float> v1;
	Vec3<float> v2;
	Vec3<float> v3;

	// Texture equation coefficients
	float aX = 1 / _dx;
	float aY = 1 / _dy;
	float aZ = 1 / _dz;
	float d  = 0;

	glBegin( GL_QUADS );
	// Face XY
	inx = 0;
	for( int indexY = 0; indexY < _segY - 1; indexY++ )
	{
		for( int indexX = 0; indexX < _segX - 1; indexX++ )
		{
			v0 = _faceXY[inx];
			v1 = _faceXY[inx + 1];
			v2 = _faceXY[inx + 1 + _segX];
			v3 = _faceXY[inx + _segX];

			float w = v2.x - v0.x;
			float h = v2.y - v0.x;

			// Top
			glNormal3f( 0.0f, 0.0f, 1.0f );

			glTexCoord2f( aX * v0.x + d, aY * v0.y + d );
			glVertex3f( v0.x, v0.y, _positiveZ );

			glTexCoord2f( aX * v1.x + d, aY * v1.y + d );
			glVertex3f( v1.x, v1.y, _positiveZ );

			glTexCoord2f( aX * v2.x + d, aY * v2.y + d );
			glVertex3f( v2.x, v2.y, _positiveZ );

			glTexCoord2f( aX * v3.x + d, aY * v3.y + d );
			glVertex3f( v3.x, v3.y, _positiveZ );

			// Bottom
			glNormal3f( 0.0f, 0.0f, -1.0f );

			glTexCoord2f( aX * v0.x + d, aY * v0.y + d );
			glVertex3f( v0.x, v0.y, _negativeZ );

			glTexCoord2f( aX * v3.x + d, aY * v3.y + d );
			glVertex3f( v3.x, v3.y, _negativeZ );

			glTexCoord2f( aX * v2.x + d, aY * v2.y + d );
			glVertex3f( v2.x, v2.y, _negativeZ );

			glTexCoord2f( aX * v1.x + d, aY * v1.y + d );
			glVertex3f( v1.x, v1.y, _negativeZ );

			inx++;
		}
		inx++;
	}

	// Face XZ
	inx = 0;
	for( int indexZ = 0; indexZ < _segZ - 1; indexZ++ )
	{
		for( int indexX = 0; indexX < _segX - 1; indexX++ )
		{
			v0 = _faceXZ[inx];
			v1 = _faceXZ[inx + 1];
			v2 = _faceXZ[inx + 1 + _segX];
			v3 = _faceXZ[inx + _segX];

			// Back
			glNormal3f( 0.0f, -1.0f, 0.0f );

			glTexCoord2f( aX * v0.x + d, aZ * v0.z + d );
			glVertex3f( v0.x, _negativeY, v0.z );

			glTexCoord2f( aX * v1.x + d, aZ * v1.z + d );
			glVertex3f( v1.x, _negativeY, v1.z );

			glTexCoord2f( aX * v2.x + d, aZ * v2.z + d );
			glVertex3f( v2.x, _negativeY, v2.z );

			glTexCoord2f( aX * v3.x + d, aZ * v3.z + d );
			glVertex3f( v3.x, _negativeY, v3.z );

			// Front
			glNormal3f( 0.0f, 1.0f, 0.0f );

			glTexCoord2f( aX * v0.x + d, aZ * v0.z + d );
			glVertex3f( v0.x, _positiveY, v0.z );

			glTexCoord2f( aX * v3.x + d, aZ * v3.z + d );
			glVertex3f( v3.x, _positiveY, v3.z );

			glTexCoord2f( aX * v2.x + d, aZ * v2.z + d );
			glVertex3f( v2.x, _positiveY, v2.z );

			glTexCoord2f( aX * v1.x + d, aZ * v1.z + d );
			glVertex3f( v1.x, _positiveY, v1.z );

			inx++;
		}
		inx++;
	}

	// Face ZY
	inx = 0;
	for( int indexY = 0; indexY < _segY - 1; indexY++ )
	{
		for( int indexZ = 0; indexZ < _segZ - 1; indexZ++ )
		{
			v0 = _faceZY[inx];
			v1 = _faceZY[inx + 1];
			v2 = _faceZY[inx + 1 + _segZ];
			v3 = _faceZY[inx + _segZ];

			// Left
			glNormal3f( -1.0f, 0.0f, 0.0f );

			glTexCoord2f( aZ * v0.z + d, aY * v0.y + d );
			glVertex3f( _negativeX, v0.y, v0.z );

			glTexCoord2f( aZ * v1.z + d, aY * v1.y + d );
			glVertex3f( _negativeX, v1.y, v1.z );

			glTexCoord2f( aZ * v2.z + d, aY * v2.y + d );
			glVertex3f( _negativeX, v2.y, v2.z );

			glTexCoord2f( aZ * v3.z + d, aY * v3.y + d );
			glVertex3f( _negativeX, v3.y, v3.z );

			// Right
			glNormal3f( 1.0f, 0.0f, 0.0f );

			glTexCoord2f( aZ * v0.z + d, aY * v0.y + d );
			glVertex3f( _positiveX, v0.y, v0.z );

			glTexCoord2f( aZ * v3.z + d, aY * v3.y + d );
			glVertex3f( _positiveX, v3.y, v3.z );

			glTexCoord2f( aZ * v2.z + d, aY * v2.y + d );
			glVertex3f( _positiveX, v2.y, v2.z );

			glTexCoord2f( aZ * v1.z + d, aY * v1.y + d );
			glVertex3f( _positiveX, v1.y, v1.z );

			inx++;
		}
		inx++;
	}
	glEnd();
}

void EPSG::Cube::drawWithNoTexCoord()
{
	int inx;
	Vec3<float> v0;
	Vec3<float> v1;
	Vec3<float> v2;
	Vec3<float> v3;

	// Texture equation coefficients
	float aX = 1 / _dx;
	float aY = 1 / _dy;
	float aZ = 1 / _dz;
	float d  = 0;

	glBegin( GL_QUADS );
	// Face XY
	inx = 0;
	for( int indexY = 0; indexY < _segY - 1; indexY++ )
	{
		for( int indexX = 0; indexX < _segX - 1; indexX++ )
		{
			v0 = _faceXY[inx];
			v1 = _faceXY[inx + 1];
			v2 = _faceXY[inx + 1 + _segX];
			v3 = _faceXY[inx + _segX];

			float w = v2.x - v0.x;
			float h = v2.y - v0.x;

			// Top
			glNormal3f( 0.0f, 0.0f, 1.0f );
			glVertex3f( v0.x, v0.y, _positiveZ );
			glVertex3f( v1.x, v1.y, _positiveZ );
			glVertex3f( v2.x, v2.y, _positiveZ );
			glVertex3f( v3.x, v3.y, _positiveZ );

			// Bottom
			glNormal3f( 0.0f, 0.0f, -1.0f );
			glVertex3f( v0.x, v0.y, _negativeZ );
			glVertex3f( v3.x, v3.y, _negativeZ );
			glVertex3f( v2.x, v2.y, _negativeZ );
			glVertex3f( v1.x, v1.y, _negativeZ );

			inx++;
		}
		inx++;
	}

	// Face XZ
	inx = 0;
	for( int indexZ = 0; indexZ < _segZ - 1; indexZ++ )
	{
		for( int indexX = 0; indexX < _segX - 1; indexX++ )
		{
			v0 = _faceXZ[inx];
			v1 = _faceXZ[inx + 1];
			v2 = _faceXZ[inx + 1 + _segX];
			v3 = _faceXZ[inx + _segX];

			// Back
			glNormal3f( 0.0f, -1.0f, 0.0f );
			glVertex3f( v0.x, _negativeY, v0.z );
			glVertex3f( v1.x, _negativeY, v1.z );
			glVertex3f( v2.x, _negativeY, v2.z );
			glVertex3f( v3.x, _negativeY, v3.z );

			// Front
			glNormal3f( 0.0f, 1.0f, 0.0f );
			glVertex3f( v0.x, _positiveY, v0.z );
			glVertex3f( v3.x, _positiveY, v3.z );
			glVertex3f( v2.x, _positiveY, v2.z );
			glVertex3f( v1.x, _positiveY, v1.z );

			inx++;
		}
		inx++;
	}

	// Face ZY
	inx = 0;
	for( int indexY = 0; indexY < _segY - 1; indexY++ )
	{
		for( int indexZ = 0; indexZ < _segZ - 1; indexZ++ )
		{
			v0 = _faceZY[inx];
			v1 = _faceZY[inx + 1];
			v2 = _faceZY[inx + 1 + _segZ];
			v3 = _faceZY[inx + _segZ];

			// Left
			glNormal3f( -1.0f, 0.0f, 0.0f );
			glVertex3f( _negativeX, v0.y, v0.z );
			glVertex3f( _negativeX, v1.y, v1.z );
			glVertex3f( _negativeX, v2.y, v2.z );
			glVertex3f( _negativeX, v3.y, v3.z );

			// Right
			glNormal3f( 1.0f, 0.0f, 0.0f );
			glVertex3f( _positiveX, v0.y, v0.z );
			glVertex3f( _positiveX, v3.y, v3.z );
			glVertex3f( _positiveX, v2.y, v2.z );
			glVertex3f( _positiveX, v1.y, v1.z );

			inx++;
		}
		inx++;
	}
	glEnd();
}

void EPSG::Cube::tesselate()
{
	float half_x = _dx / 2;
	float half_y = _dy / 2;
	float half_z = _dz / 2;

	float dx = _dx / (_segX - 1);
	float dy = _dy / (_segY - 1);
	float dz = _dz / (_segZ - 1);

	_positiveX = half_x;
	_positiveY = half_y;
	_positiveZ = half_z;

	_negativeX = -half_x;
	_negativeY = -half_y;
	_negativeZ = -half_z;


	// Face XY
	_faceXY.clear();
	float y = _negativeY;
	for( int indexY = 0; indexY < _segY; indexY++ )
	{
		float x = _negativeX;
		for( int indexX = 0; indexX < _segX; indexX++ )
		{
			_faceXY.push_back( Vec3<float>( x, y, 0 ) );
			x += dx;
		}

		y += dy;
	}

	// Face XZ
	_faceXZ.clear();
	float z = _negativeZ;
	for( int indexZ = 0; indexZ < _segZ; indexZ++ )
	{
		float x = _negativeX;
		for( int indexX = 0; indexX < _segX; indexX++ )
		{
			_faceXZ.push_back( Vec3<float>( x, 0, z ) );
			x += dx;
		}

		z += dz;
	}

	// Face ZY
	_faceZY.clear();
	y = _negativeY;
	for( int indexY = 0; indexY < _segY; indexY++ )
	{
		float z = _negativeZ;
		for( int indexZ = 0; indexZ < _segZ; indexZ++ )
		{
			_faceZY.push_back( Vec3<float>( 0, y, z ) );
			z += dz;
		}

		y += dy;
	}
}